//
// Created by lee on 2024年1月23日 0023.
//

#include <stdio.h>
#include <malloc.h>
#include "command.h"
#include "vmmath.h"

CommandRet parse_command(const unsigned char *command, long command_size) {
    Command *cmd = (Command*) malloc(sizeof(Command));
    unsigned long long int cmd_index = 0LL;
    for (long i = 0; i < command_size; ) {
        cmd[cmd_index].length = command[i];
        cmd[cmd_index].offset = 0;
        unsigned short target;
        if (command[i] == 3) {
            target = (command[i + 2] << 8) + command[i + 3];
            unsigned short source = (command[i + 4] << 8) + command[i + 5];
            cmd[cmd_index].target_type = target >> 13;
            cmd[cmd_index].source_type = source >> 13;
            cmd[cmd_index].target = target & 0b1111111111111;
            cmd[cmd_index].source = source & 0b1111111111111;
        } else if (command[i] == 2) {
            target = (command[i + 2] << 8) + command[i + 3];
            cmd[cmd_index].target_type = target >> 13;
            cmd[cmd_index].source_type = 0;
            cmd[cmd_index].target = target & 0b1111111111111;
            cmd[cmd_index].source = 0;
        }
        switch (command[i + 1]) {
            case 1:
                cmd[cmd_index].cmd = MOV;
                break;
            case 2:
                cmd[cmd_index].cmd = ADD;
                break;
            case 3:
                cmd[cmd_index].cmd = SUB;
                break;
            case 4:
                cmd[cmd_index].cmd = MUL;
                break;
            case 5:
                cmd[cmd_index].cmd = DIV;
                break;
            case 6:
                cmd[cmd_index].cmd = SHL;
                break;
            case 7:
                cmd[cmd_index].cmd = SHR;
                break;
            case 8:
                cmd[cmd_index].cmd = INT;
                break;
            case 9:
                cmd[cmd_index].cmd = JMP;
                cmd[cmd_index].offset = (char)cmd[cmd_index].target;
                break;
            case 10:
                cmd[cmd_index].cmd = JZ;
                cmd[cmd_index].offset = (char)cmd[cmd_index].target;
                break;
            case 11:
                cmd[cmd_index].cmd = JNZ;
                cmd[cmd_index].offset = (char)cmd[cmd_index].target;
                break;
            case 12:
                cmd[cmd_index].cmd = CMP;
                break;
            case 13:
                cmd[cmd_index].cmd = INC;
                break;
            case 14:
                cmd[cmd_index].cmd = DEC;
                break;
            case 15:
                cmd[cmd_index].cmd = AND;
                break;
            case 16:
                cmd[cmd_index].cmd = OR;
                break;
            case 17:
                cmd[cmd_index].cmd = NOT;
                break;
            case 18:
                cmd[cmd_index].cmd = XOR;
                break;
            case 19:
                cmd[cmd_index].cmd = PUSH;
                break;
            case 20:
                cmd[cmd_index].cmd = POP;
                break;
            default:
                cmd[cmd_index].cmd = NONE;
                break;
        }
        if (i < command_size - 1) {
            if (cmd[cmd_index].length == 3) {
                i += command[i] + 3;
            } else if (cmd[cmd_index].length == 2) {
                i += command[i] + 2;
            }
            ++cmd_index;
            Command *new_cmd = (Command*) realloc(cmd, sizeof(Command) * (cmd_index + 1));
            if (new_cmd != NULL) {
                cmd = new_cmd;
            }
        }
    }
    CommandRet ret;
    ret.cmd = cmd;
    ret.size = cmd_index;
    return ret;
}

SourceRet get_source(
        Memory memory,
        registers r,
        unsigned char source_is_memory,
        unsigned char source_type,
        unsigned short source_value
) {
    SourceRet ret;
    ret.source = 0;
    ret.ret_true = true;
    unsigned char source = 0;
    if (source_is_memory == 0) {
        if (source_type == 0) {
            source = (unsigned char) source_value;
        } else {
            source = get_register(r, (unsigned char) source_value, 0);
        }
    } else {
        if (source_type == 0) {
            if (source_value >= memory.size) {
                ret.ret_true = false;
            } else {
                source = memory.memory[source_value];
            }
        } else {
            source = memory.memory[get_register(r, (unsigned char) source_value, 0)];
        }
    }
    ret.source = source;
    return ret;
}

void reset_flags(registers *r) {
    (*r).ZF = false;
    (*r).CF = false;
    (*r).SF = false;
    (*r).OF = false;
    (*r).TF = false;
}

int execute_command(Command cmd, registers *r, Memory memory) {
    unsigned char target_is_segment = (cmd.target_type >> 2) & 1;
    unsigned char target_is_memory = (cmd.target_type >> 1) & 1;
    unsigned char target_type = cmd.target_type & 1;
    unsigned short target_value = cmd.target;
    unsigned char source_is_memory = (cmd.source_type >> 1) & 1;
    unsigned char source_type = cmd.source_type & 1;
    unsigned short source_value = cmd.source;
    if ((target_type == 1 && target_value > 7) ||
        (source_type == 1 && source_value > 7)) {
        return FALSE;
    }
    switch (cmd.cmd) {
        case MOV: {
            SourceRet source_ret = get_source(memory, *r, source_is_memory, source_type, source_value);
            if (!source_ret.ret_true) {
                return FALSE;
            }
            unsigned char source = source_ret.source;
            reset_flags(r);

            if (target_is_segment == 0) {
                if (target_is_memory == 0) {
                    if (target_type == 0) {
                        return FALSE;
                    } else {
                        update_register(r, target_value, source);
                        return TRUE;
                    }
                } else {
                    if (target_type == 0) {
                        if (target_value >= memory.size) {
                            return FALSE;
                        } else {
                            memory.memory[target_value] = source;
                            return TRUE;
                        }
                    } else {
                        memory.memory[get_register(*r, target_value, 0)] = source;
                        return TRUE;
                    }
                }
            } else {
                switch (target_value) {
                    case 0: {
                        (*r).BP = source;
                        break;
                    }
                    case 1: {
                        (*r).SP = source;
                        break;
                    }
                    case 2: {
                        (*r).SI = source;
                        break;
                    }
                    case 3: {
                        (*r).DI = source;
                        break;
                    }
                    case 4: {
                        (*r).CS = source;
                        break;
                    }
                    case 5: {
                        (*r).DS = source;
                        break;
                    }
                    case 6: {
                        (*r).SS = source;
                        break;
                    }
                    case 7: {
                        (*r).ES = source;
                        break;
                    }
                    case 8: {
                        (*r).IP = source;
                        break;
                    }
                    default:
                        return FALSE;
                }
                return TRUE;
            }
        }
        case ADD: {
            SourceRet source_ret = get_source(memory, *r, source_is_memory, source_type, source_value);
            if (!source_ret.ret_true) {
                return FALSE;
            }
            unsigned char source = source_ret.source;
            reset_flags(r);

            unsigned short target = get_register(*r, target_value, 0);
            if (target_is_memory == 0) {
                if (target_type == 0) {
                    return FALSE;
                } else {
                    if ((unsigned short)(target + source) > 0b11111111) {
                        (*r).OF = true;
                    }
                    Add a = add(target, source);
                    update_register(r, target_value, a.S);
                    (*r).CF = a.C;
                    return TRUE;
                }
            } else {
                if (target_type == 0) {
                    if (target_value >= memory.size) {
                        return FALSE;
                    } else {
                        if ((unsigned short)(memory.memory[target_value] + source) > 0b11111111) {
                            (*r).OF = true;
                        }
                        Add a = add(memory.memory[target_value], source);
                        memory.memory[target_value] = a.S;
                        (*r).CF = a.C;
                        return TRUE;
                    }
                } else {
                    unsigned char target_memory = memory.memory[target];
                    if ((unsigned short)(target_memory + source) > 0b11111111) {
                        (*r).OF = true;
                    }
                    Add a = add(target_memory, source);
                    memory.memory[target] = a.S;
                    (*r).CF = a.C;
                    return TRUE;
                }
            }
        }
        case SUB: {
            SourceRet source_ret = get_source(memory, *r, source_is_memory, source_type, source_value);
            if (!source_ret.ret_true) {
                return FALSE;
            }
            unsigned char source = source_ret.source;
            reset_flags(r);

            unsigned short target = get_register(*r, target_value, 0);
            if (target_is_memory == 0) {
                if (target_type == 0) {
                    return FALSE;
                } else {
                    unsigned short s = sub(target, source);
                    update_register(r, target_value, s);
                    return TRUE;
                }
            } else {
                if (target_type == 0) {
                    if (target_value >= memory.size) {
                        return FALSE;
                    } else {
                        unsigned short s = sub(memory.memory[target_value], source);
                        memory.memory[target_value] = s;
                        return TRUE;
                    }
                } else {
                    unsigned short s = sub(memory.memory[target], source);
                    memory.memory[target] = s;
                    return TRUE;
                }
            }
        }
        case MUL: {
            SourceRet source_ret = get_source(memory, *r, source_is_memory, source_type, source_value);
            if (!source_ret.ret_true) {
                return FALSE;
            }
            unsigned char source = source_ret.source;
            reset_flags(r);

            unsigned short target = get_register(*r, target_value, 0);
            if (target_is_memory == 0) {
                if (target_type == 0) {
                    return FALSE;
                } else {
                    if ((unsigned int)(target * source) > 0b1111111111111) {
                        (*r).OF = true;
                    }
                    unsigned short m = mul(target, source);
                    update_register(r, target_value, m);
                    return TRUE;
                }
            } else {
                if (target_type == 0) {
                    if (target_value >= memory.size) {
                        return FALSE;
                    } else {
                        unsigned short target_memory = memory.memory[target_value];
                        if ((unsigned int)(target_memory * source) > 0b1111111111111) {
                            (*r).OF = true;
                        }
                        unsigned short m = mul(target_memory, source);
                        memory.memory[target_value] = m;
                        return TRUE;
                    }
                } else {
                    unsigned short target_memory = memory.memory[target];
                    if ((unsigned int)(target_memory * source) > 0b1111111111111) {
                        (*r).OF = true;
                    }
                    unsigned short m = mul(target_memory, source);
                    memory.memory[target] = m;
                    return TRUE;
                }
            }
        }
        case DIV: {
            SourceRet source_ret = get_source(memory, *r, source_is_memory, source_type, source_value);
            if (!source_ret.ret_true) {
                return FALSE;
            }
            unsigned char source = source_ret.source;
            if (source == 0) {
                return 0;
            }
            reset_flags(r);

            unsigned short target = get_register(*r, target_value, 0);
            if (target_is_memory == 0) {
                if (target_type == 0) {
                    return FALSE;
                } else {
                    Div d = division(target, source);
                    update_register(r, 0, d.Q);
                    update_register(r, 1, d.R);
                    return TRUE;
                }
            } else {
                if (target_type == 0) {
                    if (target_value >= memory.size) {
                        return FALSE;
                    } else {
                        Div d = division(memory.memory[target_value], source);
                        update_register(r, 0, d.Q);
                        update_register(r, 1, d.R);
                        return TRUE;
                    }
                } else {
                    Div d = division(memory.memory[target], source);
                    update_register(r, 0, d.Q);
                    update_register(r, 1, d.R);
                    return TRUE;
                }
            }
        }
        case SHL: {
            SourceRet source_ret = get_source(memory, *r, source_is_memory, source_type, source_value);
            if (!source_ret.ret_true) {
                return FALSE;
            }
            unsigned char source = source_ret.source;
            reset_flags(r);

            unsigned short target = get_register(*r, target_value, 0);
            if (target_is_memory == 0) {
                if (target_type == 0) {
                    return FALSE;
                } else {
                    if ((unsigned int)(target << source) > 0b1111111111111) {
                        (*r).OF = true;
                    }
                    update_register(r, target_value, target << source);
                    return TRUE;
                }
            } else {
                if (target_type == 0) {
                    if (target_value >= memory.size) {
                        return FALSE;
                    } else {
                        unsigned short target_memory = memory.memory[target_value];
                        if ((unsigned int)(target_memory << source) > 0b1111111111111) {
                            (*r).OF = true;
                        }
                        memory.memory[target_value] = target_memory << source;
                        return TRUE;
                    }
                } else {
                    unsigned short target_memory = memory.memory[target];
                    if ((unsigned int)(target_memory << source) > 0b1111111111111) {
                        (*r).OF = true;
                    }
                    memory.memory[target] = target_memory << source;
                    return TRUE;
                }
            }
        }
        case SHR: {
            SourceRet source_ret = get_source(memory, *r, source_is_memory, source_type, source_value);
            if (!source_ret.ret_true) {
                return FALSE;
            }
            unsigned char source = source_ret.source;
            reset_flags(r);

            unsigned short target = get_register(*r, target_value, 0);
            if (target_is_memory == 0) {
                if (target_type == 0) {
                    return FALSE;
                } else {
                    update_register(r, target_value, target >> source);
                    return TRUE;
                }
            } else {
                if (target_type == 0) {
                    if (target_value >= memory.size) {
                        return FALSE;
                    } else {
                        memory.memory[target_value] >>= source;
                        return TRUE;
                    }
                } else {
                    memory.memory[target] >>= source;
                    return TRUE;
                }
            }
        }
        case INT: {
            reset_flags(r);
            unsigned short target = get_register(*r, target_value, 0);
            if (target_is_memory == 0) {
                if (target_type == 0) {
                    return target_value;
                } else {
                    return target;
                }
            } else {
                if (target_type == 0) {
                    if (target_value >= memory.size) {
                        return FALSE;
                    } else {
                        return memory.memory[target_value];
                    }
                } else {
                    return memory.memory[target];
                }
            }
        }
        case CMP: {
            SourceRet source_ret = get_source(memory, *r, source_is_memory, source_type, source_value);
            if (!source_ret.ret_true) {
                return FALSE;
            }
            unsigned char source = source_ret.source;
            reset_flags(r);

            unsigned short target = get_register(*r, target_value, 0);
            if (target_is_memory == 0) {
                if (target_type == 0) {
                    return FALSE;
                } else {
                    if (target - source == 0) {
                        (*r).ZF = true;
                    }
                    return TRUE;
                }
            } else {
                if (target_type == 0) {
                    if (target_value >= memory.size) {
                        return FALSE;
                    } else {
                        if (memory.memory[target_value] - source == 0) {
                            (*r).ZF = true;
                        }
                        return TRUE;
                    }
                } else {
                    if (memory.memory[target] - source == 0) {
                        (*r).ZF = true;
                    }
                    return TRUE;
                }
            }
        }
        case INC: {
            reset_flags(r);
            unsigned short target = get_register(*r, target_value, 0);
            if (target_is_memory == 0) {
                if (target_type == 0) {
                    return FALSE;
                } else {
                    update_register(r, target_value, target + 1);
                    if ((unsigned int)(target + 1) > 0b1111111111111) {
                        (*r).OF = true;
                    }
                    return TRUE;
                }
            } else {
                if (target_type == 0) {
                    if (target_value >= memory.size) {
                        return FALSE;
                    } else {
                        if ((unsigned int)(memory.memory[target_value] + 1) > 0b1111111111111) {
                            (*r).OF = true;
                        }
                        ++memory.memory[target_value];
                        return TRUE;
                    }
                } else {
                    if ((unsigned int)(memory.memory[target] + 1) > 0b1111111111111) {
                        (*r).OF = true;
                    }
                    ++memory.memory[target];
                    return TRUE;
                }
            }
        }
        case DEC: {
            reset_flags(r);
            unsigned short target = get_register(*r, target_value, 0);
            if (target_is_memory == 0) {
                if (target_type == 0) {
                    return FALSE;
                } else {
                    update_register(r, target_value, target - 1);
                    return TRUE;
                }
            } else {
                if (target_type == 0) {
                    if (target_value >= memory.size) {
                        return FALSE;
                    } else {
                        --memory.memory[target_value];
                        return TRUE;
                    }
                } else {
                    --memory.memory[target];
                    return TRUE;
                }
            }
        }
        case AND: {
            SourceRet source_ret = get_source(memory, *r, source_is_memory, source_type, source_value);
            if (!source_ret.ret_true) {
                return FALSE;
            }
            unsigned char source = source_ret.source;
            reset_flags(r);

            unsigned short target = get_register(*r, target_value, 0);
            if (target_is_memory == 0) {
                if (target_type == 0) {
                    return FALSE;
                } else {
                    if ((target & source) == 0) {
                        (*r).ZF = true;
                    }
                    update_register(r, target_value, target & source);
                    return TRUE;
                }
            } else {
                if (target_type == 0) {
                    if (target_value >= memory.size) {
                        return FALSE;
                    } else {
                        if ((memory.memory[target_value] & source) == 0) {
                            (*r).ZF = true;
                        }
                        memory.memory[target_value] &= source;
                        return TRUE;
                    }
                } else {
                    if ((memory.memory[target] & source) == 0) {
                        (*r).ZF = true;
                    }
                    memory.memory[target] &= source;
                    return TRUE;
                }
            }
        }
        case OR: {
            SourceRet source_ret = get_source(memory, *r, source_is_memory, source_type, source_value);
            if (!source_ret.ret_true) {
                return FALSE;
            }
            unsigned char source = source_ret.source;
            reset_flags(r);

            unsigned short target = get_register(*r, target_value, 0);
            if (target_is_memory == 0) {
                if (target_type == 0) {
                    return FALSE;
                } else {
                    if ((target | source) == 0) {
                        (*r).ZF = true;
                    }
                    update_register(r, target_value, target | source);
                    return TRUE;
                }
            } else {
                if (target_type == 0) {
                    if (target_value >= memory.size) {
                        return FALSE;
                    } else {
                        if ((memory.memory[target_value] | source) == 0) {
                            (*r).ZF = true;
                        }
                        memory.memory[target_value] |= source;
                        return TRUE;
                    }
                } else {
                    if ((memory.memory[target] | source) == 0) {
                        (*r).ZF = true;
                    }
                    memory.memory[target] |= source;
                    return TRUE;
                }
            }
        }
        case NOT: {
            reset_flags(r);
            unsigned short target = get_register(*r, target_value, 0);
            if (target_is_memory == 0) {
                if (target_type == 0) {
                    return FALSE;
                } else {
                    update_register(r, target_value, ~target);
                    return TRUE;
                }
            } else {
                if (target_type == 0) {
                    if (target_value >= memory.size) {
                        return FALSE;
                    } else {
                        memory.memory[target_value] = ~memory.memory[target_value];
                        return TRUE;
                    }
                } else {
                    memory.memory[target] = ~memory.memory[target];
                    return TRUE;
                }
            }
        }
        case XOR: {
            SourceRet source_ret = get_source(memory, *r, source_is_memory, source_type, source_value);
            if (!source_ret.ret_true) {
                return FALSE;
            }
            unsigned char source = source_ret.source;
            reset_flags(r);

            unsigned short target = get_register(*r, target_value, 0);
            if (target_is_memory == 0) {
                if (target_type == 0) {
                    return FALSE;
                } else {
                    if ((target ^ source) == 0) {
                        (*r).ZF = true;
                    }
                    update_register(r, target_value, target ^ source);
                    return TRUE;
                }
            } else {
                if (target_type == 0) {
                    if (target_value >= memory.size) {
                        return FALSE;
                    } else {
                        if ((memory.memory[target_value] ^ source) == 0) {
                            (*r).ZF = true;
                        }
                        memory.memory[target_value] ^= source;
                        return TRUE;
                    }
                } else {
                    if ((memory.memory[target] ^ source) == 0) {
                        (*r).ZF = true;
                    }
                    memory.memory[target] ^= source;
                    return TRUE;
                }
            }
        }
        case PUSH: {
            reset_flags(r);
            unsigned short target = get_register(*r, target_value, 0);
            if (target_is_memory == 0) {
                if (target_type == 0) {
                    memory.memory[(*r).DS] = target_value;
                } else {
                    memory.memory[(*r).DS] = target;
                }
                if (r->DS > 0) {
                    --(*r).DS;
                }
                return TRUE;
            } else {
                if (target_type == 0) {
                    if (target_value >= memory.size) {
                        return FALSE;
                    } else {
                        memory.memory[target_value] = memory.memory[(*r).DS];
                        if (r->DS > 0) {
                            --(*r).DS;
                        }
                        return TRUE;
                    }
                } else {
                    memory.memory[target_value] = memory.memory[target];
                    if (r->DS > 0) {
                        --(*r).DS;
                    }
                    return TRUE;
                }
            }
        }
        case POP: {
            reset_flags(r);
            unsigned short target = get_register(*r, target_value, 0);
            if (target_is_memory == 0) {
                if (target_type == 0) {
                    return FALSE;
                } else {
                    ++(*r).DS;
                    update_register(r, target_value, get_memory(memory, (*r).DS));
                    return TRUE;
                }
            } else {
                if (target_type == 0) {
                    if (target_value >= memory.size) {
                        return FALSE;
                    } else {
                        memory.memory[target_value] = memory.memory[(*r).DS];
                        ++(*r).DS;
                        return TRUE;
                    }
                } else {
                    memory.memory[target] = memory.memory[(*r).DS];
                    ++(*r).DS;
                    return TRUE;
                }
            }
        }
        default:
            return FALSE;
    }
}
